local sock = require"sock"
local Room = require "room"
local bitser = require"bitser"

local network = class("Network") {
    SERVER_VERSION = "v0.1",

    sock = nil;
    players = {},
    _player_number = 0;
    rooms = {},
    _room_number = 0;
}

function network:allocRoomName()
    local i = self._room_number + 1;
    local name = "Room" .. tostring(i);
    while self.rooms[name] ~= nil do
        i = i + 1;
        name = "Room" .. tostring(i);
    end
    return name;
end

function network:playerQuitRoom(player)
    if not player then return end;
    local room = self:getRoom(player.room)
    if room then
        if self:isHomeowners(room,player) then
            player.sock:send("QuitRoom")
            if room.player2 then
                local player2 = self:getPlayer(room.player2.name)
                if player2 then
                    player2.sock:send("QuitRoom")
                end
            end
            self.rooms[player.room] = nil;
            self._room_number = self._room_number - 1;
        else
            local player2 = self:getPlayer(room.player2)
            if player2 then
                player2.sock:send("QuitRoom")
                room.player2 = nil
            end
        end
    end
end

function network:clientQuit(client)
    local player = self:getPlayer(client)
    if player then
        self:playerQuitRoom(player)
        self.players[player.sock] = nil
        self.players[player.name] = nil;
        self._player_number = self._player_number - 1
    end
end

function network:getRoom(name)
    return self.rooms[name]
end

function network:getPlayer(name)
    if type(name) == 'string' then
        name = self.players[name]
    end
    return self.players[name];
end

function network:getPlayerRoom(name)
    local player = self:getPlayer(name)
    if player then
        local room = player.room;
        if room then
            return self:getRoom(room)
        end
    end
end

function network:isHomeowners(room,player)
    return room:getHomeowners().name == player.name
end

function network:__init__(ip,port)
    self.sock = sock.newServer(ip,port);
    self.sock:setSerialization(bitser.dumps,bitser.loads)

    self.sock:on("JoinServer",function (pack,peer)
        local name = pack.name;
        if self.players[name] == nil then
            self.players[name] = peer;
            self.players[peer] = {
                sock = peer,
                name = name,
                room = nil,
            }
            peer:send("JoinServerSucceed")
            self._player_number = self._player_number + 1
        else
            peer:send("JoinServerFail")
        end
    end)

    self.sock:on("connect",function(pack,client)
        
    end)

    self.sock:on("disconnect",function(pack,client)
        self:clientQuit(client)
    end)

    self.sock:on("disconnected",function(pack,client)
        self:clientQuit(client)
    end)

    self.sock:on("disconnecting",function(pack,client)
        self:clientQuit(client)
    end)

    self.sock:on("KeyInput",function (pack)
        --[[
            {
                room_name = string
                player_port = int // 0 = port_a 1 = part_b
                key = string
            }
        ]]
        local room = self.rooms[pack.room_name]
        if room ~= nil then
            local player_port = pack.player_port;
            local key = pack.key;
            local player1 = room.player1
            local player2 = room.player2

            player1.sock:send("KeyInput",{
                player_port = player_port,
                key = key;
            })

            player2.sock:send("KeyInput",{
                player_port = player_port,
                key = key;
            })
        end
    end)

    self.sock:on("CreateRoom",function(pack,client)
        --[[
            pack {
                room_name = string;//房间名
                homeowners = string;//房主玩家名
            }
        ]]
        local homeowners = pack.homeowners;
        local room_name = pack.room_name or self:allocRoomName();
        if(self.rooms[room_name] ~= nil) then
            client:send("CreateRoomFail")
            return;
        end
        local player = self:getPlayer(client);
        local room = Room({
            name = homeowners;
            sock = player.sock;
        });
        player.room = room_name;
        self.rooms[room_name] = room;
        self._room_number = self._room_number + 1;
        client:send("CreateRoomSucceed")
        local room_list = {}
        for name,v in pairs(self.rooms) do
            table.insert(room_list,name)
        end
        self.sock:sendToAll("GetRoomList",{list = room_list})
    end)

    self.sock:on("GetRoomList",function(pack,client)
        local room_list = {}
        for name,v in pairs(self.rooms) do
            table.insert(room_list,name)
        end
        client:send("GetRoomList",{list = room_list})
    end)

    self.sock:on("JoinRoom",function(pack,client)
        --[[
            pack {
                player_name = string
                room_name = string
            }
        ]]
        local player_name = pack.player_name;
        local room_name =  pack.room_name;
        local room = self.rooms[room_name]
        local player = self:getPlayer(player_name)
        if(room ~= nil) then
            room.player2 = {
                name = player.name;
                sock = player;
            };
            player.room = room_name
            local homeowners = room:getHomeowners()
            homeowners.sock:send("PlayerJoin",{name = player_name});
            client:send("JoinRoomSucceed",{homeowners = homeowners.name,room = room_name})
        else
            client:send("JoinRoomFail")
        end
    end)

    self.sock:on("StartRoom",function(pack,client)
        --[[
            pack {
                room_name = string;//房间名
            }
        ]]
        local room_name = pack.room_name;
        local room = self.rooms[room_name];
        if(room == nil) then
            client:send("StartRoomFail")
            return;
        end
        room:Start();
        client:send("StartRoomSucceed")
    end)
end

function network:ClearShell()
    local os_type = love.system.getOS()
    if os_type == "Windows" then
        os.execute("cls")
    else
        os.execute("clear")
    end
end

function network:PrintInfoPanel()
    print(string.format(
[[
[服务器已开启]
    IP:%s
    端口:%d
    服务器版本:%s
    玩家人数:%d
    房间数量:%d
]]
    ,
        self.sock:getAddress(),
        self.sock:getPort(),
        self.SERVER_VERSION,
        self._player_number,
        self._room_number
    ))
end

return network;
